package test.configuration.loader;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import test.client.support.thread.DefaultThreadManager.RunMode;
import test.client.support.thread.DefaultThreadManager.SleepMode;
import test.configuration.Configuration;
import test.domain.environment.Cluster;
import test.domain.environment.Database;
import test.domain.environment.MetaThread;
import test.domain.environment.MetaUser;
import test.domain.environment.file.OutFile;
import test.domain.environment.file.ResultFile;
import test.domain.environment.node.Node;
import test.domain.environment.node.Server;
import util.Debugger;

public class PropertyLoader implements Loader {
	private Log log = LogFactory.getLog(this.getClass());
	
	@Override
	public Configuration load() {
		Configuration config = new Configuration();
		
		// load properties
		Properties prop = new Properties();
		
		try {
			prop.load(new FileInputStream("resources/test.properties"));
		} catch (FileNotFoundException e) {
			log.error(e);
		} catch (IOException e) {
			log.error(e);
		}
		
		config.setTestClass(prop.getProperty("openfire.test.class"));
		
		config.setCluster(this.initializeCluster(prop));
		config.setDatabase(this.initializeDatabase(prop));
		config.setMetaUser(this.initializeMetaUser(prop));
		config.setMetaThread(this.initializeMetaThread(prop));
				
		config.setServers(this.initializeServers(prop, "openfire.test.servers"));
		
		config.setResultFile(this.initializeResultFile(prop));
		config.setOutFile(this.initializeOutFile(prop));
		
		return config;
	}
	
	private Cluster initializeCluster(Properties prop) {
		Cluster cluster = new Cluster();
		
		cluster.setMaster(Boolean.parseBoolean(prop.getProperty("openfire.test.cluster.ismaster", "true")));
		cluster.setClients(initializeNodes(prop.getProperty("openfire.test.cluster.clients")));
		
		Node listener = new Node();
		listener.setIp(prop.getProperty("openfire.test.cluster.listener.ip", "localhost"));
		listener.setPort(Integer.parseInt(prop.getProperty("openfire.test.cluster.listener.port", "9000")));
		cluster.setListener(listener);
		
		return cluster;
	}
	
	private Database initializeDatabase(Properties prop) {
		Database database = new Database();
		
		database.setType(prop.getProperty("openfire.db.type"));
		database.setClassName(prop.getProperty("openfire.db.driver"));
		database.setIp(prop.getProperty("openfire.db.ip", "127.0.0.1"));
		database.setPort(prop.getProperty("openfire.db.port"));
		database.setName(prop.getProperty("openfire.db.name", "openfire"));
		database.setUsername(prop.getProperty("openfire.db.username", "openfire"));
		database.setPassword(prop.getProperty("openfire.db.password", "welcome1"));
		
		return database;
	}
	
	private MetaUser initializeMetaUser(Properties prop) {
		MetaUser meta = new MetaUser();
		
		meta.setMaxCompanyCount(Integer.parseInt(prop.getProperty("openfire.test.companies", "1")));
		meta.setMaxUserCount(Integer.parseInt(prop.getProperty("openfire.test.accounts", "10")));
		meta.setMaxRosterCount(meta.getMaxUserCount() - 1);
		meta.setRosterStartId(Integer.parseInt(prop.getProperty("openfire.test.roster.startid", "100000")));
		meta.setUserMaxDigit(Integer.parseInt(prop.getProperty("openfire.test.user.maxdigit", "6")));
		meta.setUserPassword(prop.getProperty("openfire.test.user.password", "welcome1"));
		meta.setUserCreationDate(prop.getProperty("openfire.test.user.creationdate"));
		
		return meta;
	}
	
	private MetaThread initializeMetaThread(Properties prop) {
		MetaThread meta = new MetaThread();
		
		meta.setMaxThreadCount(Integer.parseInt(prop.getProperty("openfire.test.thread.max", "10")));
		
		meta.setSleepMode(SleepMode.valueOf(prop.getProperty("openfire.test.mode.sleep", "thread")));
		meta.setRunMode(RunMode.valueOf(prop.getProperty("openfire.test.mode.run", "ordered")));
		
		meta.setLoopSleepTime(Integer.parseInt(prop.getProperty("openfire.test.loop.sleeptime", "5000")));
		meta.setLoopMaxThreadCount(Integer.parseInt(prop.getProperty("openfire.test.loop.maxthreads", "100")));
		meta.setThreadSleepTime(Integer.parseInt(prop.getProperty("openfire.test.thread.sleeptime", "50")));
		meta.setThreadStartId(Integer.parseInt(prop.getProperty("openfire.test.thread.startid", "0")));
		
		return meta;
	}
	
	private ResultFile initializeResultFile(Properties prop) {
		ResultFile file = new ResultFile();
		
		file.setPath(prop.getProperty("openfire.test.result.filepath", "C:\\"));
		file.setName(prop.getProperty("openfire.test.result.filename", "test"));
		file.setExt(prop.getProperty("openfire.test.result.fileext", ".csv"));
		
		return file;
	}
	
	private OutFile initializeOutFile(Properties prop) {
		OutFile file = new OutFile();
		
		file.setPath(prop.getProperty("openfire.writer.filepath", "D:\\"));
		file.setName(prop.getProperty("openfire.writer.filename", "user"));
		file.setExt(prop.getProperty("openfire.writer.fileext", ".xml"));
		file.setMaxFileRow(Integer.parseInt(prop.getProperty("openfire.writer.filerows", "10")));
		
		return file;
	}
	
	private List<Node> initializeNodes(String nodeString) {
		List<Node> nodes = new ArrayList<Node>();
		
		if (nodeString.contains(",")) {
			// use multiple clients
			String[] nodeInfos = nodeString.split(",");
			for (String nodeInfo : nodeInfos) {
				nodes.add(initializeNode(nodeInfo));
			}
		}
		else {
			nodes.add(initializeNode(nodeString));
		}
		
		return nodes;
	}
	
	private Node initializeNode(String nodeInfo) {
		String[] nodeInfos = nodeInfo.split(":");
		
		Node node = new Node();
		node.setIp(nodeInfos[0]);
		node.setPort(Integer.parseInt(nodeInfos[1]));
		
		return node;
	}
	
	private List<Server> initializeServers(Properties prop, String key) {
		List<Server> servers = new ArrayList<Server>();
		
		String serverProp = prop.getProperty(key, null);
		if (serverProp == null) throw new IllegalArgumentException("Server information is invalid.");
		
		if (serverProp.contains(",")) {
			// use multiple connection managers
			String[] serverInfos = serverProp.split(",");
			for (String serverInfo : serverInfos) {
				servers.add(initializeServer(serverInfo));
			}
		}
		else {
			// use openfire server itself
			servers.add(initializeServer(serverProp));
		}
				
		return servers;
	}
	
	private Server initializeServer(String serverInfo) {
		String[] serverInfos = serverInfo.split(":");
		
		Server server = new Server();
		server.setDomain(serverInfos[0]);
		server.setPort(Integer.parseInt(serverInfos[1]));
		server.setServiceName(serverInfos[2]);
		
		return server;
	}
	
	public static void main(String[] args) {
		Loader loader = new PropertyLoader();
		Cluster cluster = loader.load().getCluster();
		
		System.out.println(Debugger.entityToString(cluster));
	}
}
